package org.dromara.admin.ocr.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.dromara.admin.domain.bo.OrderBo;
import org.dromara.admin.domain.bo.UserDetailBo;
import org.dromara.admin.domain.vo.OrderVo;
import org.dromara.admin.domain.vo.UserDetailVo;
import org.dromara.admin.enums.AuditStatus;
import org.dromara.admin.enums.SigningStatus;
import org.dromara.admin.service.IOrderService;
import org.dromara.admin.service.IUserDetailService;
import org.dromara.admin.utils.SignApiClient;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.lessor.domain.vo.HouseVo;
import org.dromara.lessor.service.IHouseService;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URL;
import java.util.*;

@Slf4j
@RequiredArgsConstructor
@Service
public class ContractSignService {

    private final IUserDetailService userDetailService;

    private final ISysUserService sysUserService;

    private final IOrderService orderService;

    private final IHouseService houseService;

    private final ISysOssService ossService;

    /** 个人三要素认证 */
    public static final String AUTH_PERSONAL = "/api/sign/authPersonMobile3";

    /** 企业三要素认证 */
    public static final String AUTH_COMPANY = "/api/sign/authCompanyMobile3";

    /** 验证码校验 */
    public static final String VERIFY_CAPTCHA = "/api/sign/captchaVerify";

    /** 重新发送认证验证码 */
    public static final String CAPTCHA_RESEND = "/api/sign/captchaResend";

    /** 添加个人用户 */
    public static final String ADD_PERSONAL_USER = "/api/sign/addPersonalUser";

    /** 添加企业用户信息 */
    public static final String ADD_ENTERPRISE_USER = "/api/sign/addEnterpriseUser";

    /** 创建合同 */
    public static final String CREATE_CONTRACT = "/api/sign/createContract";

    /** 添加签署方 */
    public static final String ADD_SIGNER = "/api/sign/addSigner";

    /** 实名认证信息查询 */
    public static final String AUTH_RECORD_INFO = "/api/sign/getAuthRecordInfo";

    /** 获取合同信息 */
    public static final String GET_CONTRACT = "/api/sign/getContract";

    /** 下载合同 */
    public static final String DOWNLOAD_CONTRACT = "/api/sign/downloadContract";


    /**
     * 获取签署进度
     * @param orderId 订单id
     * @return result 签署进度
     */
    public JsonNode querySigningProcess(Long orderId) throws Exception {
        OrderVo orderVo = orderService.queryById(orderId);
        if (BeanUtil.isEmpty(orderVo)) {
            throw new ServiceException("订单信息不存在");
        }
        if (!orderVo.getSigningStatus().equals(SigningStatus.SIGNING.getCode())) {
            throw new ServiceException("订单还未审核通过！");
        }
        String contractCode = orderVo.getContractCode(); // 合同唯一编码
        if (StrUtil.isEmpty(contractCode)) {
            throw new ServiceException("合同唯一编码不存在!");
        }
        Map<String, Object> reqData = new HashMap<>();
        reqData.put("contractNo", contractCode);
        JsonNode jsonNode = queryContractInfo(reqData);
        if (jsonNode.path("code").asInt() != 1) {
            throw new ServiceException(jsonNode.path("info").asText());
        }

        // 定位到 signUser 数组
        JsonNode result = jsonNode.path("data").path("signUser");
        // 取最后一条
        JsonNode lastSignUser = result.get(result.size() - 1);
        if (BeanUtil.isEmpty(lastSignUser)) {
            throw new ServiceException("合同签署进度用户信息获取失败!");
        }
        return lastSignUser;
    }

    /**
     * 1. 发起个人三要素认证
     * @param req 姓名 身份证 user_id
     * @return result 认证流水号
     */
    public JsonNode authPersonal(Map<String, Object> req) throws Exception {
        // 获取userId查询用户信息
        Long userId = (Long) req.get("user_id");
        UserDetailVo userDetailVo = userDetailService.queryById(userId);
        if (userDetailVo == null) {
            throw new ServiceException("用户不存在");
        }
        Map<String, Object> authData = new HashMap<>();
        authData.put("name", userDetailVo.getRealName());
        authData.put("mobile", userDetailVo.getPhonenumber());
        authData.put("id_card", userDetailVo.getIdentityCard());
        log.info("发起个人三要素认证用户id：{}，发起个人三要素认证数据: {}", userId, authData);

        // 发起个人三要素认证
        JsonNode post = SignApiClient.post(AUTH_PERSONAL, authData);
        // 判断认证结果
        if (post.path("code").asInt() != 1) {
            throw new ServiceException(post.path("info").asText());
        }
        JsonNode result = post.path("data").path("serialNo"); // 认证流水号
        log.info("取值返回认证流水号结果: {}", result);
        return result;
    }

    /**
     * 1.1 企业运营商三要素认证
     * @param req 姓名 手机号 身份证 公司名称 公司信用代码 user_id
     * @return account 用户唯一识别码
     */
    public JsonNode authEnterprise(Map<String, Object> req) throws Exception {
        // 获取userId查询用户信息
        Long userId = (Long) req.get("user_id");
        UserDetailVo userDetailVo = userDetailService.queryById(userId);
        SysUserVo sysUserVo = sysUserService.selectUserById(userId);
        if (userDetailVo == null || sysUserVo == null) {
            throw new ServiceException("用户不存在");
        }
        Map<String, Object> authData = new HashMap<>();
        authData.put("name", userDetailVo.getRealName());
        authData.put("mobile", sysUserVo.getPhonenumber());
        authData.put("id_card", userDetailVo.getIdentityCard());
        authData.put("company_name", userDetailVo.getCompanyName());
        authData.put("credit_code", userDetailVo.getCompanyCreditCode());
        log.info("发起企业三要素认证用户id：{}，发起企业三要素认证数据: {}", userId, authData);

        // 发起企业三要素认证
        JsonNode post = SignApiClient.post(AUTH_COMPANY, authData);
        // 判断认证结果
        if (post.path("code").asInt() != 1) {
            throw new ServiceException(post.path("info").asText());
        }
        JsonNode result = post.path("data").path("serialNo");
        log.info("取值返回结果: {}", result);
        return result;
    }

    /**
     * 2. 校验验证码
     * @param req 认证流水号 验证码
     * @return account 用户唯一识别码
     */
    public JsonNode verifyCaptcha(Map<String, Object> req) throws Exception {
        Map<String, Object> captchaData = new HashMap<>();
        String serialNo = req.get("serialNo").toString(); // 认证流水号
        captchaData.put("serialNo", serialNo);
        captchaData.put("captcha", req.get("captcha")); // 验证码
        log.info("校验验证码用户id：{}，校验验证码数据: {}", LoginHelper.getUserId(), captchaData);

        JsonNode post = SignApiClient.post(VERIFY_CAPTCHA, captchaData);
        // 判断认证结果
        if (post.path("code").asInt() != 1) {
            throw new ServiceException(post.path("info").asText());
        }
        // 获取用户信息判断是用户认证还是企业认证
        JsonNode dataNode = post.path("data");
        String type = dataNode.path("type").asText(""); // 不存在就返回空字符串

        if (type.contains("个人")) {
            // 个人认证
            String account = addPersonalUser(serialNo);
            // doc读取变量赋值
            req.put("account", account);
            JsonNode jsonNode = docRead(req);
            return jsonNode;
        } else if (type.contains("企业")) {
            // 企业认证
            addEnterpriseUser(serialNo);
            return null;
        } else {
            // 未知类型
            throw new ServiceException("未知认证类型");
        }
    }

    /**
     * 2.1 重新发送验证码
     * @param req 认证流水号
     * @return
     */
    public JsonNode resendCaptcha(Map<String, Object> req) throws Exception {
        Map<String, Object> captchaData = new HashMap<>();
        String serialNo = req.get("serialNo").toString(); // 认证流水号
        captchaData.put("serialNo", serialNo);
        JsonNode post = SignApiClient.post(CAPTCHA_RESEND, captchaData);
        return post;
    }

    /**
     * 3. 添加个人用户信息
     * @param serialNo 认证流水号
     * @return account 用户唯一识别码
     */
    public String addPersonalUser(String serialNo) throws Exception {
        Map<String, Object> addUserData = new HashMap<>();
        String account = "user_" + LoginHelper.getUserId(); // 用户唯一识别码
        addUserData.put("account", account);
        addUserData.put("serialNo", serialNo);
        log.info("用户实名认证成功后-[添加电子签用户]-数据：{}", JSON.toJSONString(addUserData));

        JsonNode post = SignApiClient.post(ADD_PERSONAL_USER, addUserData);
        // 判断认证结果
        int code = post.path("code").asInt();
        if (code != 1 && code != 50001) {
            throw new ServiceException(post.path("info").asText());
        }
        // 保存用户的唯一识别码
        if (code == 1) {
            // 添加用户成功
            saveAccountCode(account);
            log.info("添加用户成功");
        }
        return account;

    }

    /**
     * 3.1 添加企业用户信息
     * @param serialNo 认证流水号
     * @return account 用户唯一识别码
     */
    public String addEnterpriseUser(String serialNo) throws Exception {
        Map<String, Object> addCompanyUserData = new HashMap<>();
        String account = "company_" + LoginHelper.getUserId(); //用户唯一识别码
        addCompanyUserData.put("account", account);
        addCompanyUserData.put("serialNo", serialNo);
        log.info("企业用户实名认证成功后-[添加电子签用户]-数据：{}", JSON.toJSONString(addCompanyUserData));

        JsonNode post = SignApiClient.post(ADD_ENTERPRISE_USER, addCompanyUserData);
        // 判断认证结果
        int code = post.path("code").asInt();
        if (code != 1 && code != 50001) {
            throw new ServiceException(post.path("info").asText());
        }
        // 保存用户的唯一识别码
        if (code == 1) {
            // 添加用户成功
            saveAccountCode(account);
            log.info("添加用户成功");
        }
        return account;

    }

    /**
     * 保存用户的唯一识别码
     * @param account 用户唯一识别码
     */
    private void saveAccountCode(String account) {
        // 保存用户的唯一识别码
        if (StrUtil.isNotEmpty(account)) {
            Long userId = LoginHelper.getUserId();
            UserDetailVo userDetailVo = userDetailService.queryById(userId);
            if (BeanUtil.isEmpty(userDetailVo)) {
                throw new ServiceException("用户信息不存在！");
            }
            userDetailVo.setAccountCode(account);
            Boolean b = userDetailService.updateByBo(BeanUtil.copyProperties(userDetailVo, UserDetailBo.class));
            if (!b) {
                throw new ServiceException("电子签保存用户唯一识别码失败！");
            }
        }
    }

    /**
     * 4. doc读取变量赋值
     *
     * @param req
     * @return
     */
    public JsonNode docRead(Map<String, Object> req) {
        // 获取order_id,查询订单详情
        Long orderId = Long.valueOf(String.valueOf(req.get("order_id")));
        OrderVo orderVo = orderService.queryById(orderId);
        Long userId = orderVo.getUserId(); // 租客id
        UserDetailVo userDetailVo = userDetailService.queryById(userId);
//        if (userDetailVo.getAccountCode() == null) {
//            throw new ServiceException("用户信息未认证！");
//        }
        Long lessorUserId = orderVo.getLessorUserId(); // 房东id
        UserDetailVo lessorUserDetailVo = userDetailService.queryById(lessorUserId);
//        if (lessorUserDetailVo.getAccountCode() == null) {
//            throw new ServiceException("房东信息未认证！");
//        }
        HouseVo houseVo = houseService.queryById(orderVo.getHouseId());


        XWPFDocument doc = null;
        SysOssVo sysOssVo = null;
        String contractNo = DateUtils.dateTimeNow() + RandomUtil.randomNumbers(8); // 文件唯一标识是年月日时分秒加上随机数
        try {
            URL fileUrl = new URL("https://qishehome.oss-cn-guangzhou.aliyuncs.com/2025/09/29/a72968f1b5f04319a3f7faba49c9f078.docx");
            InputStream inputStream = fileUrl.openStream();
            doc = new XWPFDocument(inputStream);
            // 定义占位符对应关系
            Map<String, String> values = new HashMap<>();
            values.put("contractNo", contractNo);
            values.put("partyAname", lessorUserDetailVo.getRealName());
            values.put("partyBname", userDetailVo.getRealName());
            values.put("partyCname", "丙方_C");
            // 获取当前时间年月日格式
            values.put("date", DateUtil.format(DateUtil.date(), "yyyy年MM月dd日"));

            // 替换占位符
            replacePlaceholders(doc, values);

            // 替换后文档下载到本地测试
//            try (FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Admin\\Desktop\\小栖之家服务协议替换后.docx")) {
//                doc.write(outputStream);
//            }

            // 上传到 OSS
            sysOssVo = ossService.uploadByDoc(doc);
            log.info("上传oss完成{}", sysOssVo.getUrl());
            if (BeanUtil.isEmpty(sysOssVo)) {
                throw new ServiceException("上传文件失败！");
            }
        } catch (IOException e) {
            log.error("doc读取变量赋值失败：{}", e.getMessage());
            throw new ServiceException(e.getMessage());
        }

        orderVo.setContractCode(contractNo);
        orderService.updateByBo(BeanUtil.copyProperties(orderVo, OrderBo.class));
        // 创建合同数据
        Map<String, Object> createContractData = new HashMap<>();
        createContractData.put("contractNo", contractNo);  // 文件唯一标识
        createContractData.put("contractFiles", sysOssVo.getUrl()); // 替换后存储的文件地址
        createContractData.put("account", req.get("account")); // 用户唯一识别码
        // 调用创建合同接口
        JsonNode contractResult = null;
        try {
            contractResult = createContract(createContractData);
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }

        return contractResult;
    }

    /**
     * 5. 创建合同
     */
    public JsonNode createContract(Map<String, Object> req) throws Exception {
        Map<String, Object> contractData = new HashMap<>();
        contractData.put("contractNo", req.get("contractNo"));
        contractData.put("contractName", LoginHelper.getUsername() + "_小栖之家租房合同");
        contractData.put("validityTime", 15);
        contractData.put("signOrder", 2);

        Map<String, Object> contractFilesMap = new HashMap<>();
        contractFilesMap.put("1", req.get("contractFiles"));

        contractData.put("contractFiles", contractFilesMap);
        contractData.put("notifyUrl", "https://admin-api.xiaoqihome.com/contract/receiveRefundNotify"); // 回调地址
//        contractData.put("notifyUrl", ""); // 回调地址

        JsonNode post = SignApiClient.post(CREATE_CONTRACT, contractData);

        // 判断认证结果
        if (post.path("code").asInt() != 1) {
            throw new ServiceException(post.path("info").asText());
        }

        // 添加签署方
        JsonNode addSigner = addSigner(req);
        return addSigner;
    }

    /**
     * 6. 添加签署方
     */
    public JsonNode addSigner(Map<String, Object> req) throws Exception {
        Map<String, Object> signerData = new HashMap<>();
        signerData.put("contractNo", req.get("contractNo")); // 文件唯一标识
        signerData.put("account", req.get("account")); // 用户唯一识别码
        signerData.put("signType", 3);
        signerData.put("validateType", 3);
        signerData.put("faceAuthMode", 5);

        List<Map<String, Object>> signStrategyList = new ArrayList<>();

        Map<String, Object> userSignStrategyUserAutoMap = new HashMap<>();
        userSignStrategyUserAutoMap.put("attachNo", 1);
        userSignStrategyUserAutoMap.put("locationMode", 3);
        userSignStrategyUserAutoMap.put("signKey", "乙方（签名）：");
        userSignStrategyUserAutoMap.put("offsetX", 180);
        userSignStrategyUserAutoMap.put("offsetY", -5);
        signStrategyList.add(userSignStrategyUserAutoMap);
        signerData.put("signStrategyList", signStrategyList);


        // 添加甲方签署方
        Map<String, Object> signerLessorData = new HashMap<>();
        signerLessorData.put("contractNo", req.get("contractNo")); // 文件唯一标识
        signerLessorData.put("account", req.get("account")); // 用户唯一识别码
        signerLessorData.put("signType", 2);

        List<Map<String, Object>> lessorSignStrategyList = new ArrayList<>();

        Map<String, Object> lessorSignStrategyUserAutoMap = new HashMap<>();
        lessorSignStrategyUserAutoMap.put("attachNo", 1);
        lessorSignStrategyUserAutoMap.put("locationMode", 3);
        lessorSignStrategyUserAutoMap.put("signKey", "甲方（盖章）：");
        lessorSignStrategyUserAutoMap.put("offsetX", 160);
        lessorSignStrategyUserAutoMap.put("offsetY", -5);
        lessorSignStrategyList.add(lessorSignStrategyUserAutoMap);

        signerLessorData.put("signStrategyList", lessorSignStrategyList);


        // 添加丙方签署方
        Map<String, Object> signerCapitalData = new HashMap<>();
        signerCapitalData.put("contractNo", req.get("contractNo")); // 文件唯一标识
        signerCapitalData.put("account", req.get("account")); // 用户唯一识别码
        signerCapitalData.put("signType", 2);

        List<Map<String, Object>> capitalSignStrategyList = new ArrayList<>();

        Map<String, Object> capitalSignStrategyUserAutoMap = new HashMap<>();
        capitalSignStrategyUserAutoMap.put("attachNo", 1);
        capitalSignStrategyUserAutoMap.put("locationMode", 3);
        capitalSignStrategyUserAutoMap.put("signKey", "丙方（盖章）：");
        capitalSignStrategyUserAutoMap.put("offsetX", 160);
        capitalSignStrategyUserAutoMap.put("offsetY", -5);
        capitalSignStrategyList.add(capitalSignStrategyUserAutoMap);

        signerCapitalData.put("signStrategyList", capitalSignStrategyList);

        // 初始化一个空的可变列表
        List<Map<String, Object>> signerList = new ArrayList<>();
        // 添加元素
        signerList.add(signerLessorData);
        signerList.add(signerCapitalData);
        signerList.add(signerData);

        JsonNode result = SignApiClient.post(ADD_SIGNER, signerList);
        // 判断认证结果
        if (result.path("code").asInt() != 1) {
            throw new ServiceException(result.path("info").asText());
        }
        return result;

    }

    /**
     * 替换 XWPFDocument 中的占位符
     *
     * @param doc   Word文档对象
     * @param values key-value 占位符与实际值
     */
    private void replacePlaceholders(XWPFDocument doc, Map<String, String> values) {
        for (XWPFParagraph paragraph : doc.getParagraphs()) {
            for (XWPFRun run : paragraph.getRuns()) {
                String text = run.getText(0);
                if (text != null) {
                    for (Map.Entry<String, String> entry : values.entrySet()) {
                        String placeholder = "${" + entry.getKey() + "}";
                        if (text.contains(placeholder)) {
                            text = text.replace(placeholder, entry.getValue());
                            run.setText(text, 0);
                        }
                    }
                }
            }
        }
    }

    /**
     * 7. 实名认证信息查询
     */
    public JsonNode queryAuthInfo(Map<String, Object> req) {
        Map<String, Object> contractNoData = new HashMap<>();
        contractNoData.put("serialNo", req.get("serialNo"));
        try {
            return SignApiClient.post(AUTH_RECORD_INFO, contractNoData);
        } catch (Exception e) {
            throw new ServiceException("实名认证信息查询失败" + e.getMessage());
        }
    }

    /**
     * 9. 查询合同信息
     */
    public JsonNode queryContractInfo(Map<String, Object> req) throws Exception {
        Map<String, Object> contractNoData = new HashMap<>();
        contractNoData.put("contractNo", req.get("contractNo"));

        JsonNode post = SignApiClient.post(GET_CONTRACT, contractNoData);
        if (post.path("code").asInt() != 1) {
            throw new ServiceException(post.path("info").asText());
        }
        return post;

    }

    /**
     * 10. 下载合同
     */
    public JsonNode downloadContract(Map<String, Object> req) {
        Map<String, Object> contractNoData = new HashMap<>();
        contractNoData.put("contractNo", req.get("contractNo"));
        try {
            return SignApiClient.post(DOWNLOAD_CONTRACT, contractNoData);
        } catch (Exception e) {
            throw new ServiceException("下载合同失败" + e.getMessage());
        }
    }
}

